/*
 * Copyright(c) 2002, 2003, 2004, 2005, 2007 by Christian Nowak <chnowak@web.de>
 * Last update: 20th October, 2007
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "effects.h"
#include "modplay.h"
#include "mod.h"


#define FORMAT_ID	 1
#define DESCRIPTION "Amiga Pro/Soundtracker"
#define AUTHOR			"Christian Nowak <chnowak@web.de>"
#define VERSION		 "v0.03b"
#define COPYRIGHT	 "Copyright(c) 2002, 2003, 2007"


#define SAFE_MALLOC(dest, a) \
	if(!(dest = myMalloc(a))) { \
		MODFILE_Free(mod); \
		return -2; \
	}



static u32 s3m_finetunes[16] = {

	7895,7941,7985,8046,8107,8169,8232,8280,
	8363,8413,8463,8529,8581,8651,8723,8757
};



#define NUM_AMIGA_FREQS	((22 * 5) + 5)
static const struct {

	u16 amigafreq;
	u8 st3note;
} s3m_amiga2st3[] = {
	{ 0x1ac0, 0x00 }, { 0x1940, 0x01 }, { 0x17d0, 0x02 }, { 0x1680, 0x03 }, { 0x1530, 0x04 },
	{ 0x1400, 0x05 }, { 0x12e0, 0x06 }, { 0x11d0, 0x07 }, { 0x10d0, 0x08 }, { 0x0fe0, 0x09 },
	{ 0x0f00, 0x0a }, { 0x0e2c, 0x0b }, { 0x0d60, 0x10 }, { 0x0ca0, 0x11 }, { 0x0be8, 0x12 },
	{ 0x0b40, 0x13 }, { 0x0a98, 0x14 }, { 0x0a00, 0x15 }, { 0x0970, 0x16 }, { 0x08e8, 0x17 },
	{ 0x0868, 0x18 }, { 0x07f0, 0x19 }, { 0x0780, 0x1a }, { 0x0716, 0x1b }, { 0x06b0, 0x20 },
	{ 0x0650, 0x21 }, { 0x05f5, 0x22 }, { 0x05a0, 0x23 }, { 0x054f, 0x24 }, { 0x0503, 0x25 },
	{ 0x04bb, 0x26 }, { 0x0477, 0x27 }, { 0x0436, 0x28 }, { 0x03fa, 0x29 }, { 0x03c1, 0x2a },
	{ 0x0386, 0x2b }, { 0x0358, 0x30 }, { 0x0328, 0x31 }, { 0x02fa, 0x32 }, { 0x02d0, 0x33 },
	{ 0x02a6, 0x34 }, { 0x0280, 0x35 }, { 0x025c, 0x36 }, { 0x023a, 0x37 }, { 0x021a, 0x38 },
	{ 0x01fc, 0x39 }, { 0x01e0, 0x3a }, { 0x01c5, 0x3b }, { 0x01ac, 0x40 }, { 0x0194, 0x41 },
	{ 0x017d, 0x42 }, { 0x0168, 0x43 }, { 0x0153, 0x44 }, { 0x0140, 0x45 }, { 0x012e, 0x46 },
	{ 0x011d, 0x47 }, { 0x010d, 0x48 }, { 0x00fe, 0x49 }, { 0x00f0, 0x4a }, { 0x00e2, 0x4b },
	{ 0x00d6, 0x50 }, { 0x00ca, 0x51 }, { 0x00be, 0x52 }, { 0x00b4, 0x53 }, { 0x00aa, 0x54 },
	{ 0x00a0, 0x55 }, { 0x0097, 0x56 }, { 0x008f, 0x57 }, { 0x0087, 0x58 }, { 0x007f, 0x59 },
	{ 0x0078, 0x5a }, { 0x0071, 0x5b }, { 0x006b, 0x60 }, { 0x0065, 0x61 }, { 0x005f, 0x62 },
	{ 0x005a, 0x63 }, { 0x0055, 0x64 }, { 0x0050, 0x65 }, { 0x004c, 0x66 }, { 0x0047, 0x67 },
	{ 0x0043, 0x68 }, { 0x003f, 0x69 }, { 0x003c, 0x6a }, { 0x0039, 0x6b }, { 0x0035, 0x70 },
	{ 0x0032, 0x71 }, { 0x002f, 0x72 }, { 0x002d, 0x73 }, { 0x002a, 0x74 }, { 0x0028, 0x75 },
	{ 0x0025, 0x76 }, { 0x0023, 0x77 }, { 0x0021, 0x78 }, { 0x001f, 0x79 }, { 0x001e, 0x7a },
	{ 0x001c, 0x7b }, { 0x001a, 0x80 }, { 0x0019, 0x81 }, { 0x0017, 0x82 }, { 0x0016, 0x83 },
	{ 0x0015, 0x84 }, { 0x0014, 0x85 }, { 0x0012, 0x86 }, { 0x0011, 0x87 }, { 0x0010, 0x88 },
	{ 0x000f, 0x89 }, { 0x000f, 0x8a }, { 0x000e, 0x8b }, { 0x004b, 0x66 }, { 0x0474, 0x27 },
	{ 0x0500, 0x25 }, { 0x05f4, 0x22 }, { 0x054c, 0x24 }, { 0x03f8, 0x29 },
	{ 0x02b4, 0x34 }
};


static int getSamplesSize(MODFILE *mod) {
	
	int i, s;
	
	for(i = s = 0; i < mod->nSamples; i++)
		s += mod->samples[i].sampleInfo.length;
	
	return s;
}

static int calcNumOfPatterns(MODFILE *mod, int modlength) {

	int n1, n2;
	int i;
	int patternsSize = 256 *(mod->nChannels - 1);
	
	for(i = n1 = 0; i < mod->songlength; i++) {

		if(mod->playlist[i] > n1)
			n1 = mod->playlist[i];
	}
	n1++;

	n2 = modlength - getSamplesSize(mod) - 1084;
	
	return n2 % patternsSize == 0 ? n2 / patternsSize : n1;
}



/**
 * int MODFILE_SetMOD(u8 *modfile, int modlength, MODFILE *mod);
 *
 * Processes the raw data of a Protracker MOD file and copies
 * it to a structure. The structure can then be used as a handle
 * of the MOD file. The original raw data isn't needed by the
 * handle.
 *
 * Returns a value <0 on error.
 *
 * Parameters:
 * modfile	 - A pointer to the raw MOD data
 * modlength - The length of the raw data in bytes
 * mod			 - A pointer to the MOD handle
 **/
int MODFILE_SetMOD(u8 *modfile, int modlength, MODFILE *mod) {

	int ofs = 0;
	int i;
	int sampledatalen;
	int retval = 0;

	if(modfile == NULL || mod == NULL)return -1;
	mod->nInstruments = 31;
	if((memcmp(&modfile[1080], "M.K.", 4) == 0) ||
			(memcmp(&modfile[1080], "FLT4", 4) == 0) ) {
		mod->nChannels = 4;
	} else if(memcmp(&modfile[1080], "2CHN", 4) == 0) {
		mod->nChannels = 2;
	} else if(memcmp(&modfile[1080], "6CHN", 4) == 0) {
		mod->nChannels = 6;
	} else if(memcmp(&modfile[1080], "8CHN", 4) == 0) {
		mod->nChannels = 8;
	} else if(memcmp(&modfile[1080], "10CH", 4) == 0) {
		mod->nChannels = 10;
	} else if(memcmp(&modfile[1080], "12CH", 4) == 0) {
		mod->nChannels = 12;
	} else if(memcmp(&modfile[1080], "14CH", 4) == 0) {
		mod->nChannels = 14;
	} else if(memcmp(&modfile[1080], "16CH", 4) == 0) {
		mod->nChannels = 16;
	} else if(memcmp(&modfile[1080], "18CH", 4) == 0) {
		mod->nChannels = 18;
	} else if(memcmp(&modfile[1080], "20CH", 4) == 0) {
		mod->nChannels = 20;
	} else if(memcmp(&modfile[1080], "22CH", 4) == 0) {
		mod->nChannels = 22;
	} else if(memcmp(&modfile[1080], "24CH", 4) == 0) {
		mod->nChannels = 24;
	} else if(memcmp(&modfile[1080], "26CH", 4) == 0) {
		mod->nChannels = 26;
	} else if(memcmp(&modfile[1080], "28CH", 4) == 0) {
		mod->nChannels = 28;
	} else if(memcmp(&modfile[1080], "30CH", 4) == 0) {
		mod->nChannels = 30;
	} else if(memcmp(&modfile[1080], "32CH", 4) == 0) {
		mod->nChannels = 32;
	} else {
		mod->nInstruments = 15;
		mod->nChannels = 4;
	}

	mod->nChannels++;	/* Global fx channel */
	mod->nSamples = mod->nInstruments;	/* The MOD format doesn't support multisamples */

	/* 0 */
	memcpy(mod->songname, &modfile[ofs], 20);
	ofs += 20;
	/* Instruments */
	/* 20 */
	SAFE_MALLOC(mod->instruments, mod->nInstruments * sizeof(MOD_Instrument));
	memset(mod->instruments, 0, mod->nInstruments * sizeof(MOD_Instrument));
	SAFE_MALLOC(mod->samples, mod->nSamples * sizeof(MOD_Sample));
	memset(mod->samples, 0, mod->nSamples * sizeof(MOD_Sample));

	for(i = 0; i < mod->nInstruments; i++) {

		int temp, j;

		/* Name */
		memcpy(mod->samples[i].name, &modfile[ofs], 22);
		ofs += 22;
		/* Length */
		temp	= modfile[ofs++] << 8;
		temp |= modfile[ofs++];
		temp *= 2;
		mod->samples[i].sampleInfo.length = temp;
		/* Fine tune */
		temp = modfile[ofs++];
		if(temp > 7) temp -= 16;
		temp += 8;
		mod->samples[i].default_middle_c = s3m_finetunes[temp];
		/* Volume */
		mod->samples[i].default_volume = modfile[ofs++];
		/* Loop start */
		temp	= modfile[ofs++] << 8;
		temp |= modfile[ofs++];
		temp *= 2;
		mod->samples[i].sampleInfo.loop_start = temp;
		/* Loop end */
		temp	= modfile[ofs++] << 8;
		temp |= modfile[ofs++];
		temp *= 2;
		mod->samples[i].sampleInfo.loop_end = mod->samples[i].sampleInfo.loop_start + temp;

		mod->samples[i].panning = 255;
		mod->samples[i].sampleInfo.bit_16 = FALSE;
		mod->samples[i].sampleInfo.stereo = FALSE;
		mod->samples[i].sampleInfo.pingpong = FALSE;
		mod->samples[i].relative_note = 0;

		mod->samples[i].sampleInfo.looped = TRUE;
		if(temp <= 2) {

			mod->samples[i].sampleInfo.looped = FALSE;
			mod->samples[i].sampleInfo.loop_start = mod->samples[i].sampleInfo.loop_end =
			mod->samples[i].sampleInfo.length - 1;
		}

		/* Define a new instrument */
		strcpy(mod->instruments[i].name, mod->samples[i].name);
		for(j = 0; j < 256; j++) {

			mod->instruments[i].samples[j] = &mod->samples[i];
			mod->instruments[i].note[j] = j;
		}

		/* Disable instrument envelopes */
		mod->instruments[i].envPanning.enabled = FALSE;
		mod->instruments[i].envVolume.enabled = FALSE;
		mod->instruments[i].volumeFade = 32767;
	}

	/* Song length */
	mod->songlength = modfile[ofs++];
	/* CIAA speed */
	ofs++;
	/* Arrangement */
	memcpy(mod->playlist, &modfile[ofs], 128);
	ofs += 128;
	/* I.D. */
	if(mod->nInstruments != 15)
		ofs += 4;

	/* Calculate number of patterns */
	mod->nPatterns = calcNumOfPatterns(mod, modlength);
/*	for(i = mod->nPatterns = 0; i < mod->songlength; i++) {

		if(mod->playlist[i] > mod->nPatterns)
			mod->nPatterns = mod->playlist[i];
	}

	mod->nPatterns++;*/

	/* Extract the patterns */
	SAFE_MALLOC(mod->patterns, sizeof(MOD_Note*) * mod->nPatterns);
	SAFE_MALLOC(mod->patternLengths, sizeof(int) * mod->nPatterns);

	for(i = 0; i < mod->nPatterns; i++) {

		int pline, pchannel;
		u8 * curPattern;

		mod->patternLengths[i] = 64;

		/* Alloc mem for current pattern */
		SAFE_MALLOC(mod->patterns[i], sizeof(MOD_Note) * mod->nChannels * 64);
		memset(mod->patterns[i], 255, sizeof(MOD_Note) * mod->nChannels * 64);

		/* Convert MOD pattern to our format */
		curPattern = &modfile[ofs];
		for(pline = 0; pline < 64; pline++) {

			u8 *curLine = &curPattern[(mod->nChannels - 1) * 4 * pline];
			MOD_Note *globalEffect = &mod->patterns[i][(pline * mod->nChannels) + mod->nChannels - 1];

			for(pchannel = 0; pchannel < mod->nChannels - 1; pchannel++) {

	int j;
	u8 *curNote = &curLine[4 * pchannel];
	u16 note;
	u8 dnote;
	u32 instrument;
	u16 effect;
	u8 operand;
	u8 volume;

	note	=(curNote[0] & 0x0f) << 8;
	note |= curNote[1];
	instrument	= curNote[0] & 0xf0;
	instrument |=(curNote[2] & 0xf0) >> 4;
	effect = curNote[2] & 0x0f;
	operand = curNote[3];
	volume = 255;

	/* Convert note */
	dnote = 0xff;
	if(note != 0) {

		for(j = 0; j < NUM_AMIGA_FREQS; j++) {

			if(s3m_amiga2st3[j].amigafreq == note)
				dnote = s3m_amiga2st3[j].st3note;
		}
	}

	if((dnote == 0xff) &&(note)) { /* Note not found */
		return -1;
		retval = 1;
	}

	/* Convert effect */
	switch(effect) {

		case 0x00:	/* Arpeggio */
			if(operand != 0)
				effect = EFFECT_ST00;
			else
				effect = EFFECT_NONE;
			break;
		case 0x01:	/* Porta up */
			effect = EFFECT_ST10;
			break;
		case 0x02:	/* Porta down */
			effect = EFFECT_ST20;
			break;
		case 0x03:	/* Porta to note */
			effect = EFFECT_ST30;
			break;
		case 0x04:	/* Vibrato */
			effect = EFFECT_ST40;
			break;
		case 0x05:	/* Porta + volume slide */
			effect = EFFECT_ST50;
			break;
		case 0x06:	/* Vibrato + volume slide */
			effect = EFFECT_ST60;
			break;
		case 0x07:	/* Tremolo */
			effect = EFFECT_ST70;
			break;
		case 0x09:	/* Sample offset */
			effect = EFFECT_ST90;
			break;
		case 0x0a:	/* Volume slide */
			effect = EFFECT_STa0;
			break;
		case 0x0b:	/* Pattern jump */
			globalEffect->effect[0] = EFFECT_STb0;
			globalEffect->operand[0] = operand;
			effect = EFFECT_NONE;
			operand = 0;
			break;
		case 0x0c:	/* Set volume */
			effect = EFFECT_NONE;
			if(operand > 64)
				operand = 64;
			volume = operand;
			break;
		case 0x0d:	/* Pattern break */
			operand =((operand >> 4) * 10) +(operand & 0x0f);
			if(operand >= 64)
				operand = 0;

			if(globalEffect->effect[0] == EFFECT_STb0) {
				
				globalEffect->effect[0] = EFFECT_STg0;
				globalEffect->operand[1] = operand;
			} else {
				
				globalEffect->effect[0] = EFFECT_STd0;
				globalEffect->operand[0] = operand;
			}
			effect = EFFECT_NONE;
			operand = 0;
			break;
		case 0x0e:
			switch(operand >> 4) {

				int temp;

				case 0x01:	/* Fine porta up */
					effect = EFFECT_STe1;
					operand = operand & 0x0f;
					break;
				case 0x02:	/* Fine porta down */
					effect = EFFECT_STe2;
					operand = operand & 0x0f;
					break;
				case 0x04:	/* Set vibrato waveform */
					effect = EFFECT_STe4;
					operand = operand & 0x0f;
					break;
				case 0x05:	/* Set finetune */
					effect = EFFECT_STe5;
					temp = operand & 0x0f;
					if(temp > 7) temp -= 16;
						temp += 8;
					operand = operand & 0x0f;
					break;
				case 0x06:	/* Pattern loop */
					operand = operand & 0x0f;
					globalEffect->effect[0] = EFFECT_STe6;
					globalEffect->operand[0] = operand;
					effect = EFFECT_NONE;
					operand = 0;
					break;
				case 0x07:	/* Set tremolo waveform */
					effect = EFFECT_STe7;
					operand = operand & 0x0f;
					break;
				case 0x08:	/* Panning */
					effect = EFFECT_STe8;
					operand = operand & 0x0f;
					break;
				case 0x09:	/* Retrig */
					effect = EFFECT_STe9;
					operand = operand & 0x0f;
					break;
				case 0x0a:	/* Fine volume slide up */
					effect = EFFECT_STea;
					operand = operand & 0x0f;
					break;
				case 0x0b:	/* Fine volume slide down */
					effect = EFFECT_STeb;
					operand = operand & 0x0f;
					break;
				case 0x0c:	/* Note cut */
					effect = EFFECT_STec;
					operand = operand & 0x0f;
					break;
				case 0x0d:	/* Note delay */
					effect = EFFECT_STed;
					operand = operand & 0x0f;
					break;
				case 0x0e:	/* Pattern delay */
					operand = operand & 0x0f;
					globalEffect->effect[0] = EFFECT_STee;
					globalEffect->operand[0] = operand;
					effect = EFFECT_NONE;
					operand = 0;
					break;
				default:
					effect = EFFECT_NONE;
		break;
			}
			break;
		case 0x0f:	/* Set speed/tempo */
/*			if(operand < 32)
				effect = 'A' - 'A' + 1;
			else
				effect = 'T' - 'A' + 1;*/
				effect = EFFECT_STf0;
			break;
		default:
			effect = EFFECT_NONE;
			break;
	}

	j =(pline * mod->nChannels) + pchannel;
	mod->patterns[i][j].note = dnote;
	mod->patterns[i][j].instrument = instrument;
	mod->patterns[i][j].volume = volume;
	mod->patterns[i][j].effect[0] = effect;
	mod->patterns[i][j].operand[0] = operand;
			}
		}

		ofs += 4 *(mod->nChannels - 1) * 64;
	}

	/* Sample data */
	for(i = sampledatalen = 0; i < mod->nInstruments; i++) {

		sampledatalen += mod->samples[i].sampleInfo.length;
	}

	if((sampledatalen + 1084 +(mod->nPatterns * 64 *(mod->nChannels - 1) * 4)) != modlength) {

		ofs = modlength - sampledatalen;
	}

	for(i = 0; i < mod->nInstruments; i++) {

		mod->samples[i].sampleInfo.sampledata = NULL;
		if(mod->samples[i].sampleInfo.length != 0) {
			SAFE_MALLOC(mod->samples[i].sampleInfo.sampledata, mod->samples[i].sampleInfo.length);
			memcpy(mod->samples[i].sampleInfo.sampledata, &modfile[ofs], mod->samples[i].sampleInfo.length);
			ofs += mod->samples[i].sampleInfo.length;
		}
	}

	for( i = 0; i < mod->nChannels; i++) {

		mod->channels[i].voiceInfo.enabled = TRUE;
		mod->channels[i].default_panning =(((i - 1) >> 1) & 1) ^ 1 ? 86 : 167;
	}

	mod->start_speed = 6;
	mod->start_tempo = 125;
	mod->master_volume = 64;

	mod->filetype = MODULE_MOD;

	return retval;
}




/**
 * BOOL MODFILE_IsMOD(u8 *modfile, int modlength);
 *
 * Checks whether the raw data in memory is a valid
 * Protracker MOD file.
 *
 * Returns TRUE if the data is a Protracker MOD,
 * FALSE if not.
 *
 * Parameters:
 *
 * modfile	 - Pointer to the raw data to be checked
 * modlength - Length of the raw data in bytes
 **/
BOOL MODFILE_IsMOD(u8 *modfile, int modlength) {

	MODFILE temp;
	int ret;

	if((modfile == NULL) ||(modlength < 1080))
		return FALSE;

	MODFILE_Init(&temp);

	if((ret = MODFILE_SetMOD(modfile, modlength, &temp)) >= 0) {

		temp.set = TRUE;
		MODFILE_Free(&temp);
	}

	return ret == 0;
}




int MODFILE_MODGetFormatID(void) {

	return MODULE_MOD;
}

char *MODFILE_MODGetDescription(void) {

	return DESCRIPTION;
}

char *MODFILE_MODGetAuthor(void) {

	return AUTHOR;
}

char *MODFILE_MODGetVersion(void) {

	return VERSION;
}

char *MODFILE_MODGetCopyright(void) {

	return COPYRIGHT;
}
